home *** CD-ROM | disk | FTP | other *** search
- Subject: v19i101: Usenet sources archiver, Part04/04
- Newsgroups: comp.sources.unix
- Sender: sources
- Approved: rsalz@uunet.UU.NET
-
- Submitted-by: Kent Landfield <ssbell!kent>
- Posting-number: Volume 19, Issue 101
- Archive-name: rkive/part04
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then unpack
- # it by saving it into a file and typing "sh file". To overwrite existing
- # files, type "sh file -c". You can also feed this as standard input via
- # unshar, or by typing "sh <file", e.g.. If this archive is complete, you
- # will see the following message at the end:
- # "End of archive 4 (of 4)."
- # Contents: news_arc.c rkive.c
- # Wrapped by kent@ssbell on Thu Jun 1 16:19:18 1989
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- if test -f 'news_arc.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'news_arc.c'\"
- else
- echo shar: Extracting \"'news_arc.c'\" \(20172 characters\)
- sed "s/^X//" >'news_arc.c' <<'END_OF_FILE'
- X/*
- X**
- X** This software is Copyright (c) 1989 by Kent Landfield.
- X**
- X** Permission is hereby granted to copy, distribute or otherwise
- X** use any part of this package as long as you do not try to make
- X** money from it or pretend that you wrote it. This copyright
- X** notice must be maintained in any copy made.
- X**
- X**
- X** History:
- X** Creation: Tue Feb 21 08:52:35 CST 1989 due to necessity.
- X**
- X*/
- X#ifndef lint
- Xstatic char SID[] = "@(#)news_arc.c 1.1 6/1/89";
- X#endif
- X
- X#include <sys/types.h>
- X#include <sys/stat.h>
- X#include <dirent.h>
- X#include <stdio.h>
- X#include <ctype.h>
- X#include "article.h"
- X#include "cfg.h"
- X
- X/*
- X** Defines for the type of "problems"
- X** encountered in saving the articles.
- X*/
- X#define DUP_PROB 0
- X#define NAME_PROB 1
- X#define VOL_PROB 2
- X#define TYPE_PROB 3
- X
- Xint test = 0;
- Xint problem_article;
- X
- Xextern struct group_archive *newsgrp;
- Xextern int overwrite;
- X
- Xchar *strchr();
- Xchar *strcpy();
- Xchar *strcat();
- Xchar *do_problem();
- Xchar *basename();
- Xchar *suffix();
- XFILE *efopen();
- Xvoid exit();
- X
- Xget_header(filename)
- X char *filename;
- X{
- X char *dp;
- X int header_ok = 0;
- X FILE *gfp;
- X
- X init_article();
- X
- X gfp = efopen(filename,"r");
- X
- X (void) strcpy(article.newsarticle, filename);
- X
- X while (fgets(s,sizeof s,gfp) != NULL) {
- X if (debug)
- X (void) fprintf(logfp, "BUF = [%s]",s);
- X
- X if (!isalpha(*s) || (strchr(s,':') == NULL)) {
- X header_ok++;
- X if (header_ok == 2)
- X break;
- X continue;
- X }
- X
- X dp = s;
- X while (*++dp)
- X if (*dp == '\n')
- X *dp = '\0';
- X
- X store_line();
- X }
- X (void) fclose(gfp);
- X
- X if (debug)
- X dump_article();
- X}
- X
- X/*
- X** check_archive_name
- X**
- X** Assure the path specified is within the base directory
- X** specified by the archive administrator by assuring that
- X** a prankster could not have an article archived at a
- X** basedir/../../../etc/passwd
- X** location.
- X**
- X** If an absoulte path is specified in the Archive-name, it
- X** is of no concern since a "checked" base directory and
- X** volume directory are prefixed.
- X*/
- X
- Xcheck_archive_name(argstr)
- X char *argstr;
- X {
- X char *substr();
- X register char *rp;
- X register char *dp;
- X
- X /*
- X ** check to assure that the path specified
- X ** does not contain the '..' sequence.
- X */
- X
- X while ((rp = substr(argstr, "..")) != NULL) {
- X dp = rp+2;
- X while(*dp)
- X *rp++ = *dp++;
- X *rp = '\0';
- X }
- X
- X /* I know this is not necessary but what the heck.. */
- X
- X while ((rp = substr(argstr, "//")) != NULL) {
- X dp = rp+2;
- X ++rp;
- X while(*dp)
- X *rp++ = *dp++;
- X *rp = '\0';
- X }
- X
- X /*
- X ** strip the string of trailing '/'s
- X */
- X
- X dp = argstr+(strlen(argstr)-1);
- X while(*dp == '/' && dp > argstr)
- X *dp = '\0';
- X}
- X
- X/*
- X** IF YOU USE A COMPRESSION ROUTINE OTHER THAN COMPRESS
- X** OR PACK, ADD YOUR COMPRESSION SPECIFIC INFORMATION
- X** TO THE cprgs COMPRESS_TABLE ......
- X*/
- X
- Xstruct compress_tab {
- X char *com_name;
- X char *com_suffix;
- X};
- X
- Xstruct compress_tab cprgs[] = {
- X{ "compress", ".Z" },
- X{ "pack", ".z" },
- X{ NULL, 0 },
- X};
- X
- Xchar *suffix(compression)
- X char *compression;
- X {
- X struct compress_tab *ct;
- X
- X ct = &cprgs[0];
- X while ((ct->com_name) != NULL) {
- X if (strcmp(compression, ct->com_name) == 0)
- X return(ct->com_suffix);
- X ct++;
- X }
- X return("");
- X}
- X
- Xint remove_suffix(path_str)
- Xchar *path_str;
- X {
- X char *ss;
- X struct compress_tab *ct;
- X
- X /*
- X ** need to compare the filename passed in to
- X ** the compression suffix table in order to
- X ** determine if the file has a recognized,
- X ** compression suffix attached.
- X */
- X
- X ss = path_str + (strlen(path_str) -2);
- X
- X ct = &cprgs[0];
- X while ((ct->com_name) != NULL) {
- X if (strcmp(ss, ct->com_suffix) == 0) {
- X *ss = '\0';
- X return(TRUE);
- X }
- X ct++;
- X }
- X return(FALSE);
- X}
- X
- Xchar *expand_name(filename,ng)
- Xchar *filename;
- Xstruct group_archive *ng;
- X{
- X char *comp_cmd;
- X static char compress_path[MAXNAMLEN];
- X
- X (void) strcpy(compress_path, filename);
- X
- X /*
- X ** Check to see if a group specific compress was specified.
- X ** If so, then attach the suffix and return.
- X ** Else check to see if a global compress was specified. If so,
- X ** then attach the suffix and return.
- X ** If both are NULL, return filename.
- X */
- X
- X if (*(ng->compress)) {
- X comp_cmd = basename(ng->compress);
- X (void) strcat(compress_path, suffix(comp_cmd));
- X }
- X else if (*compress) {
- X comp_cmd = basename(compress);
- X (void) strcat(compress_path, suffix(comp_cmd));
- X }
- X return(compress_path);
- X}
- X
- X#ifdef REDUCE_HEADERS
- X
- Xstruct hdrstokeep {
- X char *ststr;
- X int stbytes;
- X};
- X
- Xstruct hdrstokeep hdrs[] = {
- X{ "From:", (sizeof "From:") },
- X{ "Newsgroups:", (sizeof "Newsgroups:") },
- X{ "Subject:", (sizeof "Subject:") },
- X{ "Message-ID:", (sizeof "Message-ID:") },
- X{ "Date:", (sizeof "Date:") },
- X{ NULL, 0 },
- X};
- X
- Xint keep_line(argstr)
- X char *argstr;
- X {
- X struct hdrstokeep *pt;
- X
- X pt = &hdrs[0];
- X while ((pt->ststr) != NULL) {
- X if (strncmp(argstr, pt->ststr, (pt->stbytes-1)) == 0)
- X return(TRUE);
- X pt++;
- X }
- X return(FALSE);
- X}
- X
- Xint copy(source, target)
- X char *source, *target;
- X{
- X char *strchr();
- X FILE *from, *to;
- X char fbuf[BUFSIZ];
- X int inheader;
- X
- X inheader = TRUE;
- X
- X if (verbose) {
- X (void) fprintf(logfp,"archive <%s> to <%s>\n",source,target);
- X if (test)
- X return(0);
- X }
- X if ((from = fopen(source, "r")) == NULL) {
- X (void) fprintf(errfp,"%s: cannot open %s\n",progname,source);
- X return (-1);
- X }
- X if ((to = fopen(target, "w")) == NULL) {
- X (void) fclose(from);
- X (void) fprintf(errfp,"%s: cannot create %s\n",progname,target);
- X return (-1);
- X }
- X /*
- X ** Read the source and do not print any headers
- X ** unless specified in the "keep" headers table.
- X */
- X
- X while (fgets(fbuf, BUFSIZ, from) != NULL) {
- X if (inheader) {
- X /*
- X ** Have I encountered a line without a line type ?
- X */
- X if (!isalpha(*fbuf) || (strchr(fbuf,':') == NULL))
- X inheader = FALSE;
- X
- X else {
- X /*
- X ** Determine the type of the header line and
- X ** decide if this is a line to be kept or pitched.
- X */
- X if (!keep_line(fbuf))
- X continue;
- X }
- X }
- X if (fputs(fbuf, to) == EOF) {
- X (void) unlink(target);
- X (void) fclose(from);
- X (void) fclose(to);
- X (void) fprintf(errfp,"%s: bad copy to %s\n",progname,target);
- X return (-1);
- X }
- X }
- X (void) fclose(from);
- X (void) fclose(to);
- X return(0);
- X}
- X
- X#else
- X
- Xcopy(source, target)
- X char *source, *target;
- X{
- X int from, to, ct;
- X char fbuf[BUFSIZ];
- X
- X if (verbose) {
- X (void) fprintf(logfp,"archive <%s> to <%s>\n",source,target);
- X if (test)
- X return(0);
- X }
- X if ((from = open(source, 0)) < 0) {
- X (void) fprintf(errfp,"%s: cannot open %s\n",progname,source);
- X return (-1);
- X }
- X if ((to = creat (target, 0644)) < 0) {
- X (void) close(from);
- X (void) fprintf(errfp,"%s: cannot create %s\n",progname,target);
- X return (-1);
- X }
- X while ((ct = read(from, fbuf, BUFSIZ)) != 0) {
- X if(ct < 0 || write(to, fbuf, (unsigned) ct) != ct) {
- X (void) unlink(target);
- X (void) close(from);
- X (void) close(to);
- X (void) fprintf(errfp,"%s: bad copy to %s\n",progname,target);
- X return (-1);
- X }
- X }
- X (void) close(from);
- X (void) close(to);
- X return(0);
- X}
- X
- X#endif /* REDUCE_HEADERS */
- X
- X/*
- X** mkparents:
- X**
- X** If any parent directories in
- X** fullname don't exist, create them.
- X*/
- X
- Xint mkparents(fullname)
- Xchar *fullname;
- X{
- X char *strrchr();
- X
- X register char *p;
- X char b[MAXNAMLEN];
- X int rc;
- X
- X (void) strcpy(b, fullname);
- X
- X if ((p = strrchr(b, '/')) != NULL)
- X *p = '\0';
- X else /* no directories in fullname */
- X return(0);
- X
- X if (*b == '\0') /* are we at the root ? */
- X return(0);
- X
- X if (access(b, 0) == 0)
- X return(0);
- X
- X (void) mkparents(b);
- X
- X if ((rc = makedir(b, 0755, newsgrp->owner, newsgrp->group)) != 0)
- X error("makedir failed attempting to make", b);
- X
- X return(rc);
- X}
- X
- X
- Xchar *save_article (filename,ng)
- Xchar *filename;
- Xstruct group_archive *ng;
- X{
- X char *final_path;
- X static char path[MAXNAMLEN];
- X struct stat sb;
- X
- X problem_article = FALSE;
- X path[0] = '\0';
- X
- X /*
- X ** Read the news article file to extract the
- X ** header information and fill appropriate
- X ** data structures.
- X */
- X get_header(filename);
- X
- X /*
- X ** Build the path string for the final resting spot
- X ** for the new archive member.
- X */
- X switch(ng->type) {
- X case ARCHIVE_NAME:
- X /*
- X ** The header's archive_name contains the filename in
- X ** an "elm/part06" format.
- X */
- X
- X if ((article.volume == -1) || (!header.archive_name[0]))
- X return(do_problem(NAME_PROB, ng,filename,path));
- X
- X /*
- X ** Assure the address is relative and
- X ** that some prankster can not do nasty
- X ** things to your system files by having
- X ** an Archive-name line like:
- X ** ../../../../../etc/passwd
- X */
- X
- X check_archive_name(header.archive_name);
- X
- X /*
- X ** Check to see if the article is a patch. If so,
- X ** check to see if the administrator wishes to
- X ** store the patch with the initially posted
- X ** articles. This really relys on the archive name
- X ** being correct.
- X */
- X
- X if (article.rectype == PATCH && ng->patch_type == PACKAGE)
- X /*
- X ** Store the patch in the volume specified with the
- X ** Archive-name: specified file name.
- X */
- X (void) sprintf(path,"%s/%s%d/%s", ng->location, VOLUME,
- X article.patch_volume, header.archive_name);
- X
- X else
- X (void) sprintf(path,"%s/%s%d/%s", ng->location, VOLUME,
- X article.volume, header.archive_name);
- X break;
- X case VOLUME_ISSUE:
- X /*
- X ** The article filename contains the filename in
- X ** a "v01i001" format.
- X */
- X if ((article.volume == -1) || (!article.filename[0]))
- X return(do_problem(VOL_PROB,ng,filename,path));
- X
- X (void) sprintf(path,"%s/%s%d/%s", ng->location, VOLUME,
- X article.volume, article.filename);
- X break;
- X case ARTICLE_NUMBER:
- X /*
- X ** Store in same filename - thanks news...
- X */
- X (void) sprintf(path,"%s/%s", ng->location, filename);
- X break;
- X default:
- X /*
- X ** We have got problems....
- X */
- X return(do_problem(TYPE_PROB,ng,filename,path));
- X }
- X
- X /*
- X ** Check if the file is a patch. If so, log
- X ** the patch information into the patch log
- X ** in a *non-configurable* format so that
- X ** applications can be written to access the
- X ** file's "known format".
- X */
- X
- X if (article.rectype == PATCH)
- X write_patch_log(ng,path);
- X
- X#ifdef ADD_REPOST_SUFFIX
- X if (article.repost == TRUE)
- X /*
- X ** The ADD_REPOST_SUFFIX code adds the REPOST_SUFFIX
- X ** to any file that has been indicated as a repost
- X ** by the moderator. This should not be used with
- X ** Archive-Name archiving on a filesystem with 14
- X ** character filename limits or filename truncation
- X ** can occur. You have been warned... :-(
- X **
- X ** After adding the REPOST_SUFFIX, the filename is
- X ** treated as any other file with the duplication
- X ** checks and all...
- X */
- X (void) strcat(path,REPOST_SUFFIX);
- X#endif /* ADD_REPOST_SUFFIX */
- X
- X /*
- X ** expand the path to the file to include the
- X ** compression suffix if necessary.
- X */
- X
- X final_path = expand_name(path, ng);
- X
- X /*
- X ** Make any necessary directories
- X ** along the way.
- X */
- X (void) mkparents(path);
- X
- X /*
- X ** Check to assure that there is not already
- X ** a file with the same file name. If so
- X ** copy (or archive) the file to the problems
- X ** directory.
- X **
- X ** This works for REPOSTS as well.
- X ** If the REPOST arrives and there is
- X ** no file currently at the archive location, the
- X ** REPOST is installed in the correct archive
- X ** location.
- X ** If there is a file that exists when a REPOST
- X ** arrives, the REPOST is then handled in do_problem().
- X */
- X
- X if ((stat(final_path ,&sb) == 0) && !overwrite) /* duplicate found */
- X return(do_problem(DUP_PROB,ng, filename, final_path));
- X
- X if (copy(filename,path) != 0) {
- X (void) fprintf(errfp,"copy failed for %s to %s\n",filename,path);
- X return(NULL);
- X }
- X /*
- X ** Write the filename to the .archived file in the newsgroup's
- X ** BASEDIR directory since we do not want it rearchived tomorrow.
- X */
- X write_archived(filename, path);
- X
- X /*
- X ** Return the path to the archived file.
- X */
- X return(path);
- X}
- X
- X
- X
- Xchar *do_problem(type_of_problem, ng, file, path)
- Xint type_of_problem;
- Xstruct group_archive *ng;
- Xchar *file;
- Xchar *path;
- X{
- X
- X#ifdef MV_ORIGINAL
- X char crnt_path[MAXNAMLEN];
- X#endif /*MV_ORIGINAL */
- X
- X char pmess[BUFSIZ];
- X
- X problem_article = TRUE;
- X
- X /* ALERT THE ADMINISTRATOR THAT A PROBLEM WAS ENCOUNTERED
- X **
- X ** A problem has been encountered. It could be that there is an
- X ** format mismatch or there is already a file with the same
- X ** issue/archive/msg-id name.
- X ** Copy the problem file to the problems directory.
- X ** Alert the Administrator that a problem was received.
- X */
- X
- X (void) sprintf(pmess,"PROBLEM: Article %s in %s ",file,ng->ng_name);
- X
- X switch( type_of_problem ) {
- X case NAME_PROB:
- X (void) strcat(pmess,"does not support Archive-Name Archiving\n.");
- X break;
- X case VOL_PROB:
- X (void) strcat(pmess,"does not support Volume-Issue Archiving\n.");
- X break;
- X case TYPE_PROB:
- X (void) strcat(pmess,"has an invalid archive TYPE specified\n.");
- X break;
- X case DUP_PROB:
- X if (article.repost != TRUE)
- X (void) strcat(pmess,"is a Duplicate article.\n");
- X else
- X (void) strcat(pmess,"is a Reposted article.\n");
- X (void) sprintf(pmess,"%s\tExisting Archived path - %s", pmess,path);
- X break;
- X }
- X
- X /* print the message out to the screen, crontab output, etc */
- X
- X (void) fprintf(errfp,"%s\n",pmess);
- X
- X /* log the initial detection message. */
- X
- X record_problem(pmess, file, ng);
- X
- X /* Handling Repostings.
- X **
- X ** MV_ORIGINAL
- X ** The original article is placed into a "original" directory in
- X ** the problems directory (if duplicated). The inbound reposted
- X ** article is placed into the archive in the correct position.
- X **
- X ** ADD_REPOST_SUFFIX
- X ** If ADD_REPOST_SUFFIX is defined, all reposts will have the
- X ** string specified in REPOST_SUFFIX appended to the archive
- X ** filename so that a repost of elm/part07 would appear in
- X ** the archive as elm/part07-repost prior to any compression.
- X ** The addition of the suffix was done in save_article().
- X ** Handle this as the true duplicated article that it is.
- X **
- X ** No Reposting Defines specified:
- X ** The inbound article would be placed into the archive in the
- X ** correct position only if the initial article is not in the archive.
- X ** Otherwise the reposted article is placed in the problems directory
- X ** as a normal duplicate article as it is now.
- X */
- X
- X#ifdef MV_ORIGINAL
- X if (article.repost == TRUE) {
- X /*
- X ** save the duplicated path
- X ** Caution: may have compression suffix attached
- X */
- X (void) strcpy(crnt_path, path);
- X
- X /* create the storage path for original copy */
- X /* no slash needed between Originals and crnt_path below.. */
- X
- X (void) sprintf(path,"%s/%s%s",problems_dir,"Originals",crnt_path);
- X
- X /* Display and record the actions */
- X (void) sprintf(pmess,"\tMoving %s (original)\n\tto %s",crnt_path,path);
- X (void) fprintf(errfp,"%s\n",pmess);
- X record_problem(pmess, file, ng);
- X
- X /* Make any necessary directories along the way. */
- X (void) mkparents(path);
- X
- X /* copy the original out of the way */
- X if (copy(crnt_path,path) != 0) {
- X (void) fprintf(errfp,"copy failed for %s to %s\n", crnt_path, path);
- X return(NULL);
- X }
- X
- X set_ownership(path, ng);
- X
- X /* restore the destination path for inbound article */
- X (void) strcpy(path,crnt_path);
- X
- X /* remove the existing file */
- X (void) unlink(path);
- X /*
- X ** Must assure that "path" does not have a .Z type
- X ** of suffix used in compression. If it does, it must
- X ** be removed before continuing. This is cheating and
- X ** will probably break but what the hell.
- X */
- X (void) remove_suffix(path);
- X }
- X else
- X
- X#endif /* MV_ORIGINAL */
- X
- X /*
- X ** Build the path string for the location of the article in
- X ** the problems directory. Place the file in the appropriate
- X ** directory in Article-Number format. In this manner, multiple
- X ** problems will be stored as separate files.
- X */
- X
- X (void) sprintf(path,"%s/%s/%s",problems_dir,ng->ng_name,file);
- X
- X /* Display and record the actions */
- X (void) sprintf(pmess,"\tStoring Article %s at %s\n", file, path);
- X (void) fprintf(errfp,"%s\n",pmess);
- X record_problem(pmess, file, ng);
- X
- X /* Make any necessary directories along the way. */
- X (void) mkparents(path);
- X
- X if (copy(file,path) != 0) {
- X (void) fprintf(errfp,"copy failed for %s to %s\n", file, path);
- X return(NULL);
- X }
- X
- X /*
- X ** Write the filename to the .archived file in the newsgroup's
- X ** BASEDIR directory since we do not want it rearchived tomorrow.
- X */
- X write_archived(file, path);
- X
- X /*
- X ** Return the path to the stored problem file.
- X */
- X return(path);
- X}
- X
- Xwrite_patch_log(ng, path)
- X struct group_archive *ng;
- X char *path;
- X{
- X char *sp;
- X FILE *plfp;
- X struct stat sb;
- X int hn;
- X
- X /*
- X ** The .patchlog file is used to record the
- X ** information specific to patches that come
- X ** through the newsgroup.
- X **
- X ** The format of the .patchlog file is:
- X **
- X ** path-to-patch initial-volume initial-issue volume issue
- X ** bb/patch01 22 105 23 77
- X ** v47i022 22 105 23 77
- X */
- X
- X /*
- X ** If this is the first time that an entry is written to the
- X ** patch log, add a header on top of the file for informational
- X ** purposes only...
- X */
- X if ((stat(ng->patchlog ,&sb) != 0)) {
- X plfp = efopen(ng->patchlog,"a+");
- X
- X (void) fprintf(plfp,"#\n#\tPatch log for %s\n#\n",
- X ng->ng_name);
- X
- X (void) fprintf(plfp,"# %-30s%-11s%-13s%-6s%10s\n",
- X "Path To", "Initial", "Initial",
- X "Current", "Current");
- X
- X (void) fprintf(plfp,"# %-30s%-11s%6s%13s%10s\n#\n",
- X "Patchfile", "Volume", "Issue", "Volume", "Issue");
- X (void) fclose(plfp);
- X }
- X
- X /*
- X ** Get rid of the base directory.
- X */
- X sp = path + (strlen(ng->location)+1);
- X
- X plfp = efopen(ng->patchlog,"a+");
- X (void) fprintf(plfp,"%-24s%12d%12d%12d%11d\n", sp,
- X article.patch_volume, article.patch_issue,
- X article.volume, article.issue);
- X (void) fclose(plfp);
- X}
- END_OF_FILE
- if test 20172 -ne `wc -c <'news_arc.c'`; then
- echo shar: \"'news_arc.c'\" unpacked with wrong size!
- fi
- # end of 'news_arc.c'
- fi
- if test -f 'rkive.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'rkive.c'\"
- else
- echo shar: Extracting \"'rkive.c'\" \(17099 characters\)
- sed "s/^X//" >'rkive.c' <<'END_OF_FILE'
- X/*
- X**
- X** Subsystem: USENET Sources Archiver
- X** File Name: rkive.c
- X**
- X** usage: rkive [ -dgstuvV ] [ -f config_file ] [-n newsgroup ]
- X**
- X**
- X** This software is Copyright (c) 1989 by Kent Landfield.
- X**
- X** Permission is hereby granted to copy, distribute or otherwise
- X** use any part of this package as long as you do not try to make
- X** money from it or pretend that you wrote it. This copyright
- X** notice must be maintained in any copy made.
- X**
- X** Use of this software constitutes acceptance for use in an AS IS
- X** condition. There are NO warranties with regard to this software.
- X** In no event shall the author be liable for any damages whatsoever
- X** arising out of or in connection with the use or performance of this
- X** software. Any use of this software is at the user's own risk.
- X**
- X** If you make modifications to this software that you feel
- X** increases it usefulness for the rest of the community, please
- X** email the changes, enhancements, bug fixes as well as any and
- X** all ideas to me. This software is going to be maintained and
- X** enhanced as deemed necessary by the community.
- X**
- X** Kent Landfield
- X** uunet!ssbell!kent
- X**
- X** History:
- X** Creation: Tue Feb 21 08:52:35 CST 1989 due to necessity.
- X**
- X*/
- Xchar sccsid[] = "@(#)rkive.c 1.1 6/1/89";
- X
- X#include <sys/types.h>
- X#include <sys/stat.h>
- X#include <dirent.h>
- X#include <stdio.h>
- X#include "article.h"
- X#include "cfg.h"
- X
- X/*
- X** This is necessary since the builtin makedir call uses
- X** mknod which is a superuser only call for directories.
- X*/
- X#if (!HAVE_MKDIR && !USE_SYSMKDIR)
- X#define ROOT_ONLY
- X#endif
- X
- X#define UFMT "usage: %s [ -dgstuvV ] [ -f config_file ] [ -n newsgroup ]\n"
- X
- Xint overwrite;
- Xint status_only;
- Xstruct stat sbuf;
- Xstruct group_archive *newsgrp;
- X
- Xchar tmp_mailfile[] = "/tmp/rkive.mail";
- Xchar global_mailfile[] = "/tmp/gbl.mail";
- X
- Xchar *save_article();
- Xchar *compress_file();
- Xchar *do_compress();
- Xchar *basename();
- Xchar *suffix();
- Xvoid archive();
- X
- Xchar *strcpy();
- Xchar *strcat();
- Xchar *strchr();
- XFILE *efopen();
- Xvoid exit();
- X
- Xextern int debug;
- Xextern int verbose;
- Xextern int test;
- Xextern int problem_article;
- X
- Xmain(argc, argv)
- Xint argc;
- Xchar **argv;
- X{
- X int c;
- X extern char *optarg;
- X char *nwsg = NULL;
- X
- X progname = argv[0];
- X errfp = stderr;
- X logfp = stdout;
- X
- X status_only = debug = verbose = 0;
- X test = overwrite = fill_in_defaults = 0;
- X
- X /*
- X ** Setup the default config file to be used
- X ** unless the user specifies otherwise.
- X */
- X config_file = LOCATION;
- X
- X if (argc > 1) {
- X while ((c = getopt(argc, argv, "dgstuvVn:f:")) != EOF) {
- X switch (c) {
- X case 'f':
- X config_file = optarg;
- X break;
- X case 'd':
- X debug++;
- X verbose++;
- X break;
- X case 'g':
- X fill_in_defaults++;
- X break;
- X case 'n':
- X nwsg = optarg;
- X break;
- X case 's':
- X status_only++;
- X break;
- X case 't':
- X test++;
- X verbose++;
- X break;
- X case 'u':
- X overwrite++;
- X break;
- X case 'v':
- X verbose++;
- X break;
- X case 'V':
- X version();
- X default:
- X (void) fprintf(errfp, UFMT, progname);
- X return(1);
- X }
- X }
- X }
- X
- X setup_defaults();
- X
- X init_article();
- X
- X for (c = 0; c <= num; c++) {
- X newsgrp = &group[c];
- X /*
- X ** Was a newsgroup specified on the command line ?
- X */
- X if (nwsg != NULL) {
- X if (strcmp(nwsg, newsgrp->ng_name) != 0)
- X continue;
- X }
- X archive();
- X }
- X
- X if (!status_only) {
- X /*
- X ** Mail notification of the archived members to the
- X ** list of users specified in the configuration file
- X ** and remove the file containing the archived info.
- X */
- X mail_file(mail, global_mailfile, "Complete Archive Results ");
- X (void) unlink(global_mailfile);
- X }
- X return(0);
- X}
- X
- Xvoid archive()
- X{
- X struct dirent *dp;
- X int cct;
- X DIR *dfd;
- X char *rp, *rec;
- X char *dir = ".";
- X char *new_member;
- X char *archived_file;
- X char *get_archived_rec();
- X char newsgroup_directory[MAXNAMLEN];
- X
- X#ifdef ROOT_ONLY
- X /*
- X ** check to assure that the user is root if
- X ** actual archiving is to take place. This is necessary
- X ** if there is no mkdir system call.
- X */
- X
- X if (!status_only && (getuid() != 0)) {
- X (void) fprintf(errfp, "%s: Sorry, Must be root to rkive.\n",
- X progname);
- X exit(1);
- X }
- X#endif
- X
- X /* Remove any existing temporary mail file */
- X
- X (void) unlink(tmp_mailfile);
- X cct = 0; /* counter for newsgroup message in global mail */
- X
- X /*
- X ** Assure that there something specified in the
- X ** archive location variable...
- X */
- X if (!*newsgrp->location) {
- X (void) fprintf(errfp, "SKIPPING %s: No archive location specified..\n",
- X newsgrp->ng_name);
- X return;
- X }
- X
- X /*
- X ** print out the appropriate
- X ** header for the newsgroup.
- X */
- X
- X if (debug || (verbose && status_only)) {
- X (void) fprintf(logfp,"\n\n");
- X display_group_info(newsgrp);
- X (void) fprintf(logfp,"\n");
- X }
- X else if (status_only)
- X (void) fprintf(logfp, "%s\n",newsgrp->ng_name);
- X
- X /* convert newsgroup name into a disk path */
- X
- X rp = newsgrp->ng_name;
- X
- X /*
- X ** convert all '.' to '/' to generate a path to the
- X ** newsgroup directory relative from the specified SPOOLDIR.
- X */
- X
- X while (*rp) { /* convert all */
- X if (*rp == '.') /* '.'s to '/' */
- X *rp = '/'; /* to create */
- X rp++; /* the disk */
- X } /* location */
- X
- X (void) sprintf(newsgroup_directory,"%s/%s", spooldir,newsgrp->ng_name);
- X
- X if (chdir(newsgroup_directory) != 0) {
- X (void) fprintf(errfp,"Can't change directory to %s, %s not archived\n",
- X newsgroup_directory, newsgrp->ng_name);
- X return;
- X }
- X
- X /*
- X ** Create a path to the .archived file for the newsgroup's archive.
- X ** This file is used to determine if an article has already been
- X ** archived.
- X */
- X (void) sprintf(newsgrp->arc_done,"%s/.archived",newsgrp->location);
- X
- X /*
- X ** Create a path to the .patchlog file for the newsgroup's archive.
- X ** This file is used to record patches to posted software so that
- X ** it can easily be determined what the full set of software is.
- X */
- X (void) sprintf(newsgrp->patchlog,"%s/.patchlog",newsgrp->location);
- X
- X /*
- X ** locate a file that needs to be archived. This is done by
- X ** a linear search of the directory with a linear search of
- X ** of the contents of the .archived file. If the file is not
- X ** specified in the .archived file, it has not been archived
- X ** before and we can proceed with the archiving.
- X */
- X if ((dfd = opendir(dir)) == NULL) {
- X (void) fprintf(errfp, "can't open %s\n", newsgroup_directory);
- X return;
- X }
- X while ((dp = readdir(dfd)) != NULL) {
- X if (strcmp(dp->d_name,".") == 0
- X || strcmp(dp->d_name,"..") == 0)
- X continue;
- X
- X if (stat(dp->d_name, &sbuf) != 0) {
- X (void) fprintf(errfp, "can't stat %s/%s\n",
- X newsgroup_directory, dp->d_name);
- X continue;
- X }
- X
- X /*
- X ** If its not a regular file, we cannot archive it.
- X */
- X
- X else if ((sbuf.st_mode & S_IFMT) != S_IFREG)
- X continue;
- X
- X /*
- X ** If the user has specified that a quick status
- X ** listing should be produced then hop to it....
- X */
- X
- X if (status_only) {
- X if ((rec = get_archived_rec(dp->d_name)) == NULL)
- X (void) fprintf(logfp,"\t<%s> Awaiting Archiving\n",dp->d_name);
- X else if ((rp = strchr(rec,' ')) == NULL)
- X (void) fprintf(logfp,"\t<%s> Archived\n",dp->d_name);
- X else {
- X rp++;
- X *(rp-1) = '\0';
- X (void) fprintf(logfp,"\t<%s> Archived as <%s>\n",rec,rp);
- X }
- X continue;
- X }
- X
- X /*
- X ** Archiving from here on out.
- X */
- X
- X if (!needs_to_be_archived(dp->d_name))
- X continue;
- X
- X if ((new_member = save_article(dp->d_name,newsgrp)) != NULL) {
- X archived_file = compress_file(new_member,newsgrp);
- X set_ownership(archived_file,newsgrp);
- X
- X /*
- X ** If a problem has been encountered,
- X ** the function do_problem handles
- X ** the logging, and notifying.
- X */
- X
- X if (!problem_article) {
- X log_activities(archived_file,newsgrp);
- X build_index(new_member,newsgrp);
- X notify_users(archived_file,newsgrp,cct++);
- X }
- X }
- X else
- X (void) fprintf(logfp,"Unable to archive %s/%s!!!\n",
- X newsgrp->ng_name, dp->d_name);
- X }
- X (void) closedir(dfd);
- X
- X if (!status_only) {
- X /* Remove the expired entries from the .archived file */
- X /* stored in the newsgroup's BASEDIR directory. */
- X
- X remove_expired();
- X
- X /* Mail notification of the archived members to the */
- X /* list of users specified in the configuration file */
- X /* and remove the file containing the archived info. */
- X
- X mail_file(newsgrp->mail_list, tmp_mailfile, newsgrp->ng_name);
- X (void) unlink(tmp_mailfile);
- X }
- X return;
- X}
- X
- X/*
- X** Notify Users of Archiving.
- X** If users have been specified to be informed, check to see
- X** if they have requested a specific logging format. If so
- X** use the specified format to notify the user. If not, use
- X** "file archived at path" message.
- X*/
- Xnotify_users(filename,ng,num_msgs)
- Xchar *filename;
- Xstruct group_archive *ng;
- Xint num_msgs;
- X{
- X /*
- X ** Are there users specified in the
- X ** newsgroup section ?
- X */
- X if ( *(ng->mail_list) ) {
- X if ( *(ng->logformat) )
- X logit(tmp_mailfile, ng->logformat, filename);
- X else
- X logit(tmp_mailfile, DEFAULT_LOG_FORMAT, filename);
- X }
- X
- X /*
- X ** Are there users specified in the
- X ** global section ?
- X */
- X if ( *mail ) {
- X if (num_msgs == 0) /* print the newsgroup name out */
- X logit(global_mailfile, "\n\t\t:%G:\n",filename);
- X if (*log_format)
- X logit(global_mailfile, log_format,filename);
- X else
- X logit(global_mailfile, DEFAULT_LOG_FORMAT, filename);
- X }
- X}
- X
- X/*
- X** Log_activities
- X**
- X** There are two possible logfiles that need to be written.
- X** The group specific logfile (ng->logfile) and the global
- X** log. If it has been configured to use a specific format
- X** for the logging, do so. Else, just record the fact the
- X** file was sucessfully archived and the date.
- X*/
- Xlog_activities(filename,ng)
- Xchar *filename;
- Xstruct group_archive *ng;
- X{
- X long clock;
- X long time();
- X char *ctime();
- X
- X char logbuf[BUFSIZ];
- X char dms_date[30];
- X
- X if ( !*(ng->logformat) || !*log_format) {
- X clock = time((long *)0);
- X (void) strcpy(dms_date, ctime(&clock));
- X *(dms_date+(strlen(dms_date)-1)) = '\0';
- X (void) sprintf(logbuf,"%s archived %s",filename, dms_date);
- X }
- X
- X if ( *(ng->logformat) )
- X logit(ng->logfile, ng->logformat, filename);
- X else
- X logit(ng->logfile, logbuf, filename);
- X
- X if ( *log_format )
- X logit(log, log_format, filename);
- X else
- X logit(log, logbuf, filename);
- X}
- X
- X/*
- X** logit
- X**
- X** This function is used to append a logfile record
- X** if there is a logfile name specified.
- X**
- X*/
- X
- Xlogit(filename, format_of_log, arch_file)
- Xchar *filename;
- Xchar *format_of_log;
- Xchar *arch_file;
- X{
- X FILE *fp, *fopen();
- X
- X if ( *(filename) ) { /* Is a logfile specified ? */
- X if ((fp = fopen(filename,"a")) != NULL) {
- X format_output(fp, format_of_log, arch_file, ARCHIVE);
- X (void) fclose(fp);
- X }
- X }
- X}
- X
- X
- Xset_ownership(filename,ng)
- Xchar *filename;
- Xstruct group_archive *ng;
- X{
- X if (verbose) { /* Print out the actions about to be preformed */
- X (void) fprintf(logfp,"chown\t<%d> <%s>\n", ng->owner, filename);
- X (void) fprintf(logfp,"chgrp\t<%d> <%s>\n", ng->group, filename);
- X }
- X
- X if (!test) { /* chown the owner/group to the desired values */
- X if (chown(filename,ng->owner, ng->group) != 0)
- X error("Can't change ownership of", filename);
- X }
- X
- X if (verbose) { /* Print out the actions about to be preformed */
- X (void) fprintf(logfp,"chmod\t<%o> <%s>\n", ng->modes, filename);
- X }
- X
- X if (!test) { /* change the file modes to the specified modes */
- X if (chmod(filename,ng->modes) != 0)
- X error("Can't change modes of", filename);
- X }
- X}
- X
- Xmail_file(user_list, file_to_mail, nwsgrp)
- Xchar *user_list;
- Xchar *file_to_mail;
- Xchar *nwsgrp;
- X{
- X char *list, *name;
- X char cmdstr[80];
- X
- X /* Is there a list of users to mail to ? */
- X if ( !*user_list || (strlen(user_list) == 0))
- X return;
- X
- X /* Was there a notification file created ? */
- X if (stat(file_to_mail, &sbuf) != 0)
- X return;
- X
- X name = user_list;
- X do {
- X if ((list = strchr(name,',')) != NULL) {
- X list++;
- X *(list-1) = '\0';
- X }
- X
- X#ifdef SUBJECT_LINE
- X (void) sprintf(cmdstr, "%s -s '%s' %s < %s",
- X MAIL, nwsgrp, name, file_to_mail);
- X#else
- X (void) sprintf(cmdstr, "%s %s < %s", MAIL, name, file_to_mail);
- X#endif
- X if (verbose)
- X (void) fprintf(logfp,"Mailing %s Archived results to %s\n",
- X nwsgrp, name);
- X if (!test)
- X (void) system(cmdstr);
- X
- X name = list;
- X
- X } while (name != NULL);
- X return;
- X}
- X
- Xbuild_index(filename,ng)
- Xchar *filename;
- Xstruct group_archive *ng;
- X{
- X if (*(ng->index)) { /* Is there a newsgroup index file ? */
- X if (*(ng->indformat)) /* Yes, Is there a index file format? */
- X logit(ng->index, ng->indformat, filename);
- X else if (*index_format) /* No, is there a global format ? */
- X logit(ng->index, index_format, filename);
- X else /* No, use the default index format */
- X logit(ng->index, DEFAULT_INDEX_FORMAT, filename);
- X }
- X
- X if (*index) { /* Is there a global index file ? */
- X if (*index_format) /* Yes, Is there a global file format ? */
- X logit(index, index_format, filename);
- X else /* No, so use the default index format */
- X logit(ng->index, DEFAULT_INDEX_FORMAT , filename);
- X }
- X}
- X
- X
- Xchar *compress_file(filename,ng)
- Xchar *filename;
- Xstruct group_archive *ng;
- X{
- X static char compressed[MAXNAMLEN];
- X
- X (void) strcpy(compressed, filename); /* store the filename */
- X
- X /* Check to see if a group specific compress was specified. */
- X /* If so, then execute the command with the filename passed in. */
- X /* Else check to see if a global compress was specified. If so, */
- X /* then execute the command with the filename passed in. */
- X /* If both are NULL, no compression is done. */
- X
- X if (*(ng->compress))
- X (void) strcat(compressed, do_compress(ng->compress, filename));
- X else if (*compress)
- X (void) strcat(compressed, do_compress(compress, filename));
- X
- X return(compressed);
- X}
- X
- Xchar *do_compress(packit,filename)
- Xchar *packit;
- Xchar *filename;
- X{
- X char *comp_cmd;
- X char cmd[BUFSIZ];
- X
- X (void) sprintf(cmd,"%s %s", packit, filename);
- X
- X /*
- X ** get the basename of the command to use.
- X */
- X comp_cmd = basename(packit);
- X
- X if (verbose)
- X (void) fprintf(logfp,"%s %s\n", comp_cmd, filename);
- X
- X if (!test)
- X (void) system(cmd);
- X
- X return(suffix(comp_cmd));
- X}
- X
- X
- X/*
- X** Record_problem()
- X** This function is used to log problems encountered
- X** to the designated parties.
- X*/
- Xrecord_problem(msg_fmt,filename,ng)
- Xchar *msg_fmt;
- Xchar *filename;
- Xstruct group_archive *ng;
- X{
- X /*
- X ** This function is used in the event that a problem
- X ** has occurred during archiving. It mails a message
- X ** to the newsgroup speecified list and it mails a
- X ** message to the globally specified users.
- X **
- X ** It then logs the fact into both the newsgroup
- X ** and the global logfiles if they have been specified.
- X */
- X
- X if ( *(ng->mail_list) )
- X logit(tmp_mailfile, msg_fmt, filename);
- X
- X if ( *mail )
- X logit(global_mailfile, msg_fmt,filename);
- X
- X logit(ng->logfile, msg_fmt, filename);
- X logit(log, msg_fmt, filename);
- X}
- END_OF_FILE
- if test 17099 -ne `wc -c <'rkive.c'`; then
- echo shar: \"'rkive.c'\" unpacked with wrong size!
- fi
- # end of 'rkive.c'
- fi
- echo shar: End of archive 4 \(of 4\).
- cp /dev/null ark4isdone
- MISSING=""
- for I in 1 2 3 4 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 4 archives.
- rm -f ark[1-9]isdone
- else
- echo You still need to unpack the following archives:
- echo " " ${MISSING}
- fi
- ## End of shell archive.
- exit 0
-
-